L09: Roura

linux.edumach.cz


Východiska

V Unixových systémech je mnoho převážně jednoúčelových příkazů. Není to nedostatek, ale naopak přednost. Můžete si z nich poskládat komplexnější příkaz, který udělá přesně to, co požadujete. V tom spočívá pravá síla a “svoboda” Unixových systémů.

“Svoboda” v tomto kontextu znamená, že v okenních systémech jste omezeni pouze na možnosti nástrojů, které autoři systému vytvořili. V terminálu si je jednoduše poskládáte na míru sami.

⬇️ Pracovní soubory ke stažení

$ wget https://linux.edumach.cz/files/cvicne.tar.gz
$ tar xvfz cvicne.tar.gz
$ cd cvicne
$ ls -l

Roura

Pomocí roury (pipe) se přesměruje výstup jednoho příkazu (stdout) na vstup druhého příkazu (stdin). Zapisuje se znakem | (svislá čára, na Windows AltGr+W):

$ prikaz1 | prikaz2

A pokud je to potřeba, výstup druhého na vstup třetího atd. (dvě a více rour se nazývá "kolona):

$ prikaz1 | prikaz2 | prikaz3

Někteří lidé to přirovnávají k lidské řeči. Máme spoustu slov (příkazů), které samy o sobě označují (vykonávají) pouze jednu činnost. Ale díky tomu, že je můžeme kombinovat do vět (přesměrování a roury), můžeme pomocí těch jednoduchých slov (příkazů) vyjádřit složitější myšlenky (provádět složitější činnosti).

A pokud je potřeba, pak výstup posledního do souboru:

$ prikaz1 | prikaz2 | prikaz3 > soubor

A pokud by to stále nestačilo, můžete si napsat svůj skript nebo vlastní program v Pythonu nebo jazyce C. Záleží jen na vašich schopnostech.

💾 Ukázkový příklad 1

Dejme tomu, že máme seznam barev v souboru colors (soubor je bez přípony), který chceme (1) setřídit podle abecedy, (2) vyřadit duplicity a (3) zobrazit prvních 5 řádků.

  1. Obsah souboru (pro jistotu) vypíšeme: cat colors
  2. Zkusíme si vypsat setříděný výstup: sort colors
  3. Nyní totéž bez duplicit: sort -u colors
  4. Teď si vyzkoušíme příkaz head: head colors (defaultně vypíše prvních 10 řádků)
  5. Omezíme na 5 řádků: head -n 5 colors
$ cat colors
$ sort colors
$ sort -u colors
$ head colors
$ head -n 5 colors

Nastal čas “slepit” oba příkazy rourou do jednoho:

$ sort -u colors | head -n 5

Příkaz sort setřídí seznam lidí a s parametrem -u také data zbaví duplicit (viz manuálová stránka příkazu).
Příkaz sort předá data příkazu head, který vypíše prvních 5 řádků  -n 5. Výstup by měl být takovýto:

black
blue
cyan
green  
magenta

Skvělé, že? Je libo číslovaný výstup? Není problém:

$ sort -u  colors | head -n 5 | cat -n

Příkaz cat převezme výstup příkazu head (proto za příkazem cat není název souboru), očísluje řádky -n a výstup vypíše do terminálu (standardního výstupu):

	1 black
	2 blue
	3 cyan
	4 green
	5 magenta

💾 Ukázkový příklad 2

Existuje jeden velice užitečný příkaz wc (word count), který vrací počet znaků, slov a řádků souboru:

$ wc alice.txt
   3601  26470 148576 alice.txt

Příkaz vrátil toto: soubor alice.txt má 3601 řádků, 26470 slov a 148576 znaků. S parametrem -l vrací pouze počet řádků (viz man wc).

U souborů je to snadné, Co když ale budeme chtít spočítat počet souborů a složek (tzn. řádků) v nějaké složce? Příkazu wc nemůžeme podstrčit složku jako argument. S ní neumí pracovat, umí pracovat pouze se soubory. Z toho, co už víme bychom mohli vytvořit konstrukci:

$ ls /home > pocet.txt  
$ wc -l pocet.txt

První příkaz přesměruje výstup ls do pomocného souboru pocet.txt. Druhý vypíše jeho počet řádků. To je ale zbytečně složité. S tomuto účelu skvěle poslouží roura:

$ ls /home | wc -l

Jak to funguje? ls /home vypíše seznam objektů (souborů a složek) a tento výstup pošle “rourou” (znak |) na vstup příkazu wc. Ten je převezme a vypíše jejich počet na stdout.

Příkaz tee

Nedostatkem přesměrování výstupu je skutečnost, že se odesílá buď na obrazovku nebo do souboru. Nikdy ne na oba výstupy najednou. Toto řeší příkaz tee. Obecná syntaxe je:

tee [-a] [soubor]

Příkaz tee čte stdin, posílá je na stdout a zároveň ho zapisuje do souboru. S přepínačem -a je přidává na konec. Typické využití:

příkaz | tee [-a] soubor  
příkaz 2> /dev/null | tee [-a] soubor

Příkaz less

Příkaz less vypisuje obsah textového souboru včetně stránkování a hledání:

$ less alice.txt
$ less -N alice.txt

S přepínačem -N zobrazuje čísla řádků. Pro pohyb se používají běžné klávesy jako šipky nahoru a dolů, PgDown, PgUp. Klávesa q ukončí výpis (quit). Hledat řetězec v textu je možné zadáním / a hledaného řetězce (/vzor). Přechod na další výskyt se provede klávesou n, předchozí klávesou N.

Příkaz less tedy čistě technicky čte stdin a stránkovaně jej posílá na stdout. Toho lze využít: Pomocí roury stránkovat jakýkoliv výstup libovolného příkazu:

$ ls -l /bin | less  
$ ls -l /bin | less -N

Příkaz sort

Příkaz sort setřídí obsah souboru nebo spojí více souborů do jednoho setříděného souboru. Bez parametrů seřadí řádky souboru podle kódu ASCII: čísla, velká písmena, malá písmena.
Užitečné přepínače:

💾 Vyzkoušejte si to na souborech cisla a kurzy.

Příkaz wc

Příkaz wc (word count) spočítá a vypíše počet řádků, slov a znaků souboru (pochopitelně textovém). Za slovo je považován řetězec znaků oddělený mezerou, tabulátorem nebo znakem nového řádku (CR, LF). Není-li zadáno jméno souboru, čte ze standardního vstupu (stdin).

$ wc soubor  
99 458 3114 soubor

Pomocí parametrů příkazu lze určit, které informace má vypisovat:

Například následující příkaz vypíše počet přihlášených uživatelů:

$ who | wc -l  
5

💾 Vyzkoušejte si to na souboru alice.txt a teploty.csv.

Příkaz tr

Velmi užitečný příkaz. Funguje jako filtr – čte proud bytů ze standardního vstupu a výsledek zapisuje na standardní výstup, aniž by měnil vstupní soubor. Bez roury se neobejdete:

tr "<hledat>" "<nahradit>"

Znaky vstupu, které se vyskytují v prvním řetězci budou nahrazeny odpovídajícími znaky druhého řetězce:

$ cat file | tr "a" "A"            # a za A  
$ cat file | tr "iy" "IY"          # i a y za I a Y  
$ cat file | tr "," "."            # čárka za tečku  
$ cat file | tr "[a-z]" "[A-Z]"    # na velká písmena  
$ cat file | tr "\t" " "           # tabulátory na mezery

Pro odstranění znaku slouží přepínač -d:

$ cat  file | tr -d ","            # odstraní všechny čárky

Existují i zápisy typu [:upper:] (velká písmena), které zastoupí zápis [A-Z]. Hledejte v manuálové stránce příkazu tr. Příklad alternativního zápisu:

$ cat  file | tr [a-z] [A-Z]  
$ cat  file | tr [:lower:] [:upper:]

💾 Několik příkladů:

Změní velikost písmen v souboru:

$ tr "[:lower:]" "[:upper:]" < /etc/resolv.conf

Můžeme třeba zaměnit znak e za o:

$ cat /etc/resolv.conf | tr "e" "o"

Můžete také například chtít naformátovat ošklivý nepřehledný soubor tak, že mezery nahradíme tabulátory. Vstup vypadá takto:

$ cat  osklivy_soubor
1. 2,13 8,3 OK
2. 0,00 1,3 ERROR
3. 2,16 5,5 OK

Hezčí výstup:

$ cat  osklivy_soubor | tr " " "\t"
1. 2,13 8,3 OK
2. 0,00 1,3 ERROR
3. 2,16 5,5 OK

Řádkové skripty

Řádkové skripty jsou malé programy nebo skripty napsané v jazyce jako je sed (stream editor) nebo awk (pattern scanning and processing language), které pracují na úrovni textových řádků v unixovém prostředí. Tyto skripty jsou obvykle používány k manipulaci s textovými daty, filtrování, transformaci a zpracování vstupních proudů textu v jednotlivých řádcích.

Sed se často používá pro jednoduché úpravy, jako je nahrazování řetězců, odstraňování nebo filtrování řádků, zatímco awk je výkonnější nástroj s možností komplexní analýzy a zpracování textu na základě definovaných vzorů a akcí.

Tyto nástroje jsou klíčové pro automatizaci úloh manipulace s textovými daty v linuxovém prostředí a jsou často používány v kombinaci s dalšími unixovými nástroji a skriptovacími jazyky.

Příkaz sed

sed postupně čte řádky vstupního souboru, provádí zadané příkazy a výsledek zapisuje na standardní výstup. Velmi často se používá pro nahrazování řetězců v souborech:

$ sed 's/vzor/novy-text/g' soubor

nahradí při výpisu textu všechny výskyty vzoru za novy-text.

Pro uchování změn je buď potřeba přesměrovat standardní výstup do souboru (nehrozí riziko přepsání zdrojového souboru) nebo využít přepínač -i, který čtený soubor přepíše.

$ sed 's/vzor/novy-text/g' soubor > soubor2
$ sed -i 's/vzor/novy-text/g' soubor

Sed dále podporuje regulární výrazy. Jejich využitím získáváme mocného pomocníka.

Několik příkladů:

Toto nám odstraní všechny komentáře v souboru (začínají znakem #) a zároveň odstraní prázdné řádky:

$ sed '/^#/d; /^ *$/d'  /cesta/k/souboru

Ještě jedna praktická ukázka využití sedu. Chceme zkopírovat část souboru. Konce dat, které nás zajímají, jsou určené nějakým výrazem:

$ sed '/koncovy_retezec/q' /etc/rc.conf

Možnosti využití sedu jsou velmi rozsáhlé: www.howtogeek.com/666395/how-to-use-the-sed-command-on-linux/.

Příkaz awk

Awk (pattern scanning and processing language) je také často používaný program pro práci s textem. Obecná syntaxe:

awk 'program' [soubor] 

Část 'program' je série pravidel, která mohou obsahovat vzor, akci nebo obojí. Akce je uzavřena do { } a uzavřena apostrofy (jednoduché uvozovky). Když je ve vstupu nalezen vzor, vykoná se příslušná akce.

Příklad níže zobrazí jen druhý sloupec souboru:

$ awk '{print $2}' soubor 

awk implicitně očekává sloupce oddělené mezerami. Jiný oddělovač se zapisuje za přepínač -F"<znak>":

$ awk -F";" '{print $1,$3}' data.csv 

Příkaz výše vypíše první a třetí sloupec souboru data.csv, kde oddělovač je znak středník (;).

Další příklad: Řekněme, že data.txt je soubor s daty, kdy každý řádek obsahuje několik údajů. Příkazem

$ awk '$2 ~ "abc" {print $1, "\t", $4}' data.txt 

vytisknete 1. a 4. údaj na každém řádku, jehož 2. údaj obsahuje text abc.

Ukázka souboru před zpracováním:

1   abc123   X   value1 
2   def456   Y   value2 
3   abc789   Z   value3 
4   ghi000   X   value4 
5   abc001   Y   value5 

Ukázka výstupu po zpracování:

1    value1 
3    value3 
5    value5 

Příkaz vybral jen ty řádky, kde druhý sloupec obsahuje text abc, a následně vypsal první a čtvrtý sloupec. Mezi ně vložil znak tabulátoru \t.

Stejně jako sed i příkaz awk toho umí výrazně víc: www.howtogeek.com/562941/how-to-use-the-awk-command-on-linux/

💾 Cvičení

Níže je 10 úkolů. Napište příkazy, které by danou činnost vykonaly. Využijte manuálové stránky příkazů.

  1. Vypište prvních 12 řádků souboru text.txt (příkaz head).

  2. Vypište poslední 2 řádky souboru /var/log/vysledky.log (příkaz tail).

  3. Zjistěte, zda mají soubory file_a na file_b stejný obsah (příkaz cmp). Přidejte slovní odpověď, jak se to pozná.

  4. Vypište obsah pracovního adresáře (široký výpis, včetně skrytých souborů), použijte stránkovací příkaz less.

  5. Vypište obsah souboru /etc/passwd, a to setříděně (reverzní, tzn. opačné pořadí).

  6. Vypište obsah svého home adresáře včetně skrytých souborů. Výsledek přesměrujte do souboru seznam.txt.

  7. Zjistěte počet řádků souboru /etc/fstab.

  8. Příkaz tr "eth0" "eth1" zamění všechny výskyty prvního řetězce druhým. Napište příkaz, který změny provede a uloží je do souboru eth.txt.

  9. Zjistěte, kolik souborů a adresářů je ve vašem domovském adresáři, včetně podadresářů.

  10. Najděte všechny soubory s příponou .txt ve vašem pracovním adresáři a jeho podadresářích (příkaz find).

  11. Napište příkaz, který vypíše seznam souborů v adresáři /var/log, přesměruje chybový výstup do /dev/null a standardní výstup pošle rourou do grep "error", výsledek pak zapíše do chybne_zaznamy.txt.